
#include <BFont>
#include <BPainter>
#include <BImage>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BEvent>
#include <BScrollBar>
#include <BSystem>
#include <BFile>

#include <BTextEdit>
#include "member_BTextEdit.h"
#include "member_BTextFormat.h"
#include "member_BWidget.h"
#include "member_BStyle.h"
#include "member_BObject.h"

using namespace BWE;

#define member					(*(member_BTextEdit*)_ptr)
#define member_allocate()		_ptr = new member_BTextEdit(this)
#define member_release()		delete (member_BTextEdit*)_ptr

BTextEdit::BTextEdit(BWidget* parent) : BScrollArea(parent)
{
	member_allocate();
	this->setAreaPolicy(Policy_Growing, Policy_Preferred);
}
BTextEdit::BTextEdit(const BString& title, BWidget* parent) : BScrollArea(title, parent)
{
	member_allocate();
	this->setAreaPolicy(Policy_Growing, Policy_Preferred);
}
BTextEdit::~BTextEdit()
{
	member_release();
}

bool BTextEdit::load(const BString& fileName)
{
	BFile file(fileName);
	if (!file.open(IO_ReadText))
		return false;
	file.read(member.text);
	member.resetBlocks();
	member.changed = false;
	member.dirty = true;
	if (this->checks(Signal_TextChanged))
	{
		this->emit(Signal_TextChanged, member.text);
	}
	member.freshFormat();
	this->fresh();
	return true;
}
bool BTextEdit::save(const BString& fileName) const
{
	BFile file(fileName);
	if (!file.open(IO_WriteText))
		return false;
	return file.write(this->text());
}
void BTextEdit::clear()
{
	if (member.blocklst.size())
	{
		member.text.clear();
		if (member.hoverBlock.valid())
		{
			member.hoverBlock.reset();
			this->emit(Signal_LineHovered, 0);
		}
		if (member.firstBlock < member.lastBlock)
		{
			member.firstBlock.reset();
			member.lastBlock.reset();
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
		}
		else if (member.inputBlock.valid() && member.inputBlock->count > 0)
		{
			member.inputBlock->count = 0;
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
		}
		member.resetBlocks();
		member.changed = false;
		member.dirty = true;
		member.freshFormat();
		if (this->checks(Signal_TextChanged))
		{
			this->emit(Signal_TextChanged, member.text);
		}
		this->emit(Signal_Clear);
		this->fresh();
	}
}

bool BTextEdit::setLineSpacing(int lineSpacing)
{
	if (member.lineSpacing != lineSpacing)
	{
		member.lineSpacing = lineSpacing;
		for (int i = 0; i < member.blocklst.size(); i++)
		{
			ILineBlock& block = member.blocklst[i];
			block.lineSpacing = member.lineSpacing;
			block.dirty = true;
		}
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
int BTextEdit::lineSpacing() const
{
	return member.lineSpacing;
}

bool BTextEdit::setLineHeight(int lineHeight)
{
	if (member.lineHeight != lineHeight)
	{
		member.lineHeight = lineHeight;
		for (int i = 0; i < member.blocklst.size(); i++)
		{
			ILineBlock& block = member.blocklst[i];
			block.lineHeight = member.lineHeight;
			block.dirty = true;
		}
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
int BTextEdit::lineHeight() const
{
	return member.lineHeight;
}

void BTextEdit::setLineFeed(bool lineFeed)
{
	if (member.lineFeed != lineFeed)
	{
		member.lineFeed = lineFeed;
		for (int i = 0; i < member.blocklst.size(); i++)
		{
			member.blocklst[i].dirty = true;
		}
		member.dirty = true;
		this->fresh();
	}
}
bool BTextEdit::lineFeed() const
{
	return member.lineFeed;
}

void BTextEdit::setWordWrap(bool wordWrap)
{
	if (member.wordWrap != wordWrap)
	{
		member.wordWrap = wordWrap;
		for (int i = 0; i < member.blocklst.size(); i++)
		{
			member.blocklst[i].dirty = true;
		}
		member.dirty = true;
		this->fresh();
	}
}
bool BTextEdit::wordWrap() const
{
	return member.wordWrap;
}

void BTextEdit::setBlinkTime(BReal blinkTime)
{
	if (member.blinkTime != blinkTime)
	{
		member.blinkTime = blinkTime;
		this->fresh();
	}
}
BReal BTextEdit::blinkTime() const
{
	return member.blinkTime;
}

void BTextEdit::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		if (readOnly)
			this->setCursor(Cursor_None);
		else
			this->setCursor(Cursor_Ibeam);
		member.inputBlock.reset();
		member.dirty = true;
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
	}
}
bool BTextEdit::readOnly() const
{
	return member.readOnly;
}

bool BTextEdit::setStackSize(int stackSize)
{
	if (stackSize > 9999)
		stackSize = 9999;
	if (member.stackSize != stackSize)
	{
		member.stackSize = stackSize;
		if (stackSize == 0)
		{
			member.operaStack.clear();
		}
		if (stackSize > 0)
		{
			if (member.operaStack.size() > stackSize)
			{
				int count = member.operaStack.size() - stackSize;
				member.operaStack.remove(0, count);
			}
		}
		return true;
	}
	return false;
}
int BTextEdit::stackSize() const
{
	return member.stackSize;
}

bool BTextEdit::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		if (member.hoverBlock.valid())
		{
			member.hoverBlock.reset();
			this->emit(Signal_LineHovered, 0);
		}
		if (member.firstBlock < member.lastBlock)
		{
			member.firstBlock.reset();
			member.lastBlock.reset();
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
		}
		else if (member.inputBlock.valid() && member.inputBlock->count > 0)
		{
			member.inputBlock->count = 0;
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
		}
		member.resetBlocks();
		member.changed = false;
		member.dirty = true;
		member.freshFormat();
		if (this->checks(Signal_TextChanged))
		{
			member.freshText();
			this->emit(Signal_TextChanged, member.text);
		}
		this->fresh();
		return true;
	}
	return false;
}
const BString& BTextEdit::text() const
{
	if (member.changed)
	{
		member.freshText();
	}
	return member.text;
}

bool BTextEdit::setLineText(int line, const BString& text)
{
	auto it = member.blocklst.seek(line);
	if (it.empty())
		return false;
	ILineBlock& block = *it;
	int end = text.find('\n');
	text.substr(block.text, 0, end + 1);
	if (!block.text.endWith('\n'))
		block.text << '\n';
	block.reset();
	block.font = this->realFont();
	block.dirty = true;
	member.changed = true;
	member.dirty = true;
	if (this->checks(Signal_TextChanged))
	{
		member.freshText();
		this->emit(Signal_TextChanged, member.text);
	}
	if (this->checks(Signal_LineChanged))
	{
		this->emit(Signal_LineChanged, line);
	}
	member.freshFormat();
	this->fresh();
	return true;
}
const BString& BTextEdit::lineText(int line) const
{
	return member.blocklst(line).text;
}

bool BTextEdit::addText(const BString& text)
{
	if (text.size())
	{
		member.appendBlocks(text);
		member.dirty = true;
		member.freshFormat();
		if (this->checks(Signal_TextChanged))
		{
			member.freshText();
			this->emit(Signal_TextChanged, member.text);
		}
		this->fresh();
		return true;
	}
	return false;
}
bool BTextEdit::inputText(const BString& text)
{
	if (text.size())
	{
		BReal now = bTime();
		member.operaInsert(now, text);
		member.removeSelection();
		member.insertText(text);
		member.emitEdited(now);
		return true;
	}
	return false;
}
bool BTextEdit::insertText(int index, const BString& text)
{
	if (index < 0 || text.empty())
		return false;
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(index))
		{
			index -= it->range.min();
			return insertText(it.index(), index, text);
		}
	}
	return addText(text);
}
bool BTextEdit::insertText(int line, int index, const BString& text)
{
	if (text.empty())
		return false;

	ILineBlockIterator block = member.blocklst.seek(line);
	if (block.empty())
		return false;

	if (index < 0 || index > block->cells.size())
		return false;

	if (text.contain('\n'))
	{
		int pos = 0;
		BString line;
		while (pos < text.size())
		{
			pos = text.line(line, pos);
			block->insert(line);
			int split_pos = block->cells.rfind('\n', block->tail() - 1);
			if (split_pos >= 0)
			{
				member.blocklst.insert(block + 1);
				block->index = split_pos + 1;
				block->count = block->cells.size() - block->index;
				BString nextstr;
				block->collect(nextstr);
				block->removeSelection();
				block += 1;
				block->reset(nextstr);
				block->font = this->realFont();
				block->dirty = true;
			}
		}
	}
	else
	{
		block->insert(index, text);
	}
	member.dirty = true;
	member.changed = true;
	member.freshFormat();
	if (this->checks(Signal_TextChanged))
	{
		member.freshText();
		this->emit(Signal_TextChanged, member.text);
	}
	this->fresh();
	return true;
}
bool BTextEdit::removeText(int index, int count)
{
	if (index < 0 || count < 1)
		return false;
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(index))
		{
			index -= it->range.min();
			return removeText(it.index(), index, count);
		}
	}
	return false;
}
bool BTextEdit::removeText(int line, int index, int count)
{
	if (line < 0 || index < 0 || count < 1)
		return false;

	ILineBlockIterator block = member.blocklst.seek(line);
	if (block.empty())
		return false;
	
	while (block->cells.size() < count)
	{
		count -= block->cells.size();
		member.blocklst.erase(block);
		block = member.blocklst.seek(line);
	}
	block->remove(index, count);
	if (member.inputBlock.index() > line)
		member.inputBlock.reset();
	member.dirty = true;
	member.changed = true;
	member.freshFormat();
	if (this->checks(Signal_TextChanged))
	{
		member.freshText();
		this->emit(Signal_TextChanged, member.text);
	}
	this->fresh();
	return true;
}

bool BTextEdit::removeLine(int line) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
	{
		if (member.inputBlock.index() >= line)
			member.inputBlock.reset();
		if (member.hoverBlock.index() >= line)
			member.hoverBlock.reset();
		if (member.pressBlock.index() >= line)
			member.pressBlock.reset();
		if (member.firstBlock.index() >= line)
			member.firstBlock.reset();
		if (member.lastBlock.index() >= line)
			member.lastBlock.reset();
		member.blocklst.erase(it);
		member.showBegin.reset();
		member.showEnd.reset();
		member.dirty = true;
		member.changed = true;
		if (this->checks(Signal_TextChanged))
		{
			member.freshText();
			this->emit(Signal_TextChanged, member.text);
		}
		this->fresh();
		return true;
	}
	return false;
}
bool BTextEdit::removeLines(int line, int count) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
	{
		auto end = member.blocklst.seek(count);
		if (member.inputBlock.index() >= line)
			member.inputBlock.reset();
		if (member.hoverBlock.index() >= line)
			member.hoverBlock.reset();
		if (member.pressBlock.index() >= line)
			member.pressBlock.reset();
		if (member.firstBlock.index() >= line)
			member.firstBlock.reset();
		if (member.lastBlock.index() >= line)
			member.lastBlock.reset();
		member.blocklst.erase(it, end);
		member.showBegin.reset();
		member.showEnd.reset();
		member.dirty = true;
		member.changed = true;
		if (this->checks(Signal_TextChanged))
		{
			member.freshText();
			this->emit(Signal_TextChanged, member.text);
		}
		this->fresh();
		return true;
	}
	return false;
}

bool BTextEdit::setInputPos(int line, int index)
{
	auto it = member.blocklst.seek(line);
	if (it.valid() && it->cells.check(index))
	{
		member.inputBlock = it;
		member.inputBlock->index = index;
		member.unselect();
		return true;
	}
	return false;
}
bool BTextEdit::setInputPos(int index)
{
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(index))
		{
			index -= it->range.min();
			member.inputBlock = it;
			member.inputBlock->index = index;
			member.unselect();
			return true;
		}
	}
	return false;
}
int BTextEdit::inputPos() const
{
	int index = -1;
	if (member.inputBlock.valid())
	{
		int min = member.inputBlock->range.min();
		index = member.inputBlock->index + min;
	}
	return index;
}
int BTextEdit::inputPos(int& line) const
{
	if (member.inputBlock.valid())
	{
		line = member.inputBlock.index();
		return member.inputBlock->index;
	}
	line = -1;
	return -1;
}
int BTextEdit::inputLine() const
{
	if (member.inputBlock.valid())
	{
		return member.inputBlock.index();
	}
	return -1;
}

bool BTextEdit::select(int min, int max)
{
	if (member.selectBlocks(min, max))
	{
		this->emit(Signal_SelectionChanged, BRange(min, max));
		this->fresh();
		return true;
	}
	return false;
}
bool BTextEdit::select(const BRange& range)
{
	if (member.selectBlocks(range.min(), range.max()))
	{
		this->emit(Signal_SelectionChanged, range);
		this->fresh();
		return true;
	}
	return false;
}
bool BTextEdit::selectLines(int firstLine, int lastLine)
{
	ILineBlockIterator firstBlock = member.blocklst.seek(firstLine);
	ILineBlockIterator lastBlock = member.blocklst.seek(lastLine);
	if (firstBlock.empty() || lastBlock.empty())
		return false;
	if (firstBlock == lastBlock)
	{
		member.inputBlock = firstBlock;
		member.inputBlock->index = 0;
		member.inputBlock->count = firstBlock->cells.size();
		member.firstBlock.reset();
		member.lastBlock.reset();
		member.pressBlock = firstBlock;
		member.pressIndex = 0;
	}
	else
	{
		member.firstBlock = firstBlock;
		member.lastBlock = lastBlock;
		for (auto it = member.firstBlock; it <= member.lastBlock; ++it)
		{
			it->index = 0;
			it->count = it->cells.size();
		}
	}
	if (this->checks(Signal_SelectionChanged))
	{
		BRange range;
		member.selectedRange(range);
		this->emit(Signal_SelectionChanged, range);
	}
	this->fresh();
	return true;
}

BRange BTextEdit::selectedRange() const
{
	BRange range;
	if (member.firstBlock.valid())
	{
		int min = member.firstBlock->range.min() + member.firstBlock->index;
		int max = member.lastBlock->range.min() + member.lastBlock->index + member.lastBlock->count;
		range.set(min, max);
	}
	else if (member.inputBlock.valid())
	{
		int min = member.inputBlock->range.min() + member.inputBlock->index;
		int max = min + member.inputBlock->count;
		range.set(min, max);
	}
	return range;
}
BString BTextEdit::selectedText() const
{
	BString text;
	if (member.firstBlock.valid())
	{
		for (auto it = member.firstBlock; it <= member.lastBlock; it++)
		{
			it->collect(text);
		}
	}
	else if (member.inputBlock.valid())
	{
		member.inputBlock->collect(text);
	}
	return text;
}
bool BTextEdit::removeSelection()
{
	if (member.hasSelection())
	{
		BReal time = bTime();
		member.operaRemove(time);
		member.removeSelection();
		member.emitEdited(time);
		return true;
	}
	return false;
}

int BTextEdit::codeCount() const
{
	auto it = member.blocklst.rbegin();
	if (it.valid())
		return it->range.max();
	return 0;
}
int BTextEdit::indexAt(const BPoint& point) const
{
	const BRect& crect = this->centerRect();
	if (crect.contain(point))
	{
		for (auto it = member.showBegin; it < member.showEnd; it++)
		{
			if (it->visible && it->rect.contain(point))
			{
				int index = it->indexAt(point);
				index += it->range.min();
				return index;
			}
		}
		return -1;
	}
	for (auto it = member.blocklst.begin(); it.empty(); it++)
	{
		if (it->visible && it->rect.contain(point))
		{
			int index = it->indexAt(point);
			index += it->range.min();
			return index;
		}
	}
	return -1;
}

BRect BTextEdit::codeRect(int index) const
{
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(index))
		{
			index -= it->range.min();
			BRect rect = it->cells[index].rect;
			rect += it->rect.pos();
			return rect;
		}
	}
	return BRect();
}
BRect BTextEdit::codeRect(int line, int index) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
	{
		BRect rect = it->cells(index).rect;
		rect += it->rect.pos();
		return rect;
	}
	return BRect();
}

BCode BTextEdit::code(int index) const
{
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(index))
		{
			index -= it->range.min();
			return it->cells[index].code;
		}
	}
	return BCode();
}
BCode BTextEdit::code(int line, int index) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
	{
		return it->cells[index].code;
	}
	return BCode();
}

int BTextEdit::lineCount() const
{
	return member.blocklst.size();
}
int BTextEdit::lineAt(const BPoint& point) const
{
	for (auto it = member.showBegin; it < member.showEnd; it++)
	{
		if (it->rect.top() <= point.y() && it->rect.bottom() >= point.y())
		{
			return it.index();
		}
	}
	return -1;
}
const BRange& BTextEdit::lineRange(int line) const
{
	return member.blocklst(line).range;
}
const BRect& BTextEdit::lineRect(int line) const
{
	return member.blocklst(line).rect;
}

bool BTextEdit::setLineVisible(int line, bool visible)
{
	if (member.blocklst.check(line))
	{
		bool dirty = false;
		auto it = member.blocklst.seek(line);
		if (it->visible != visible)
		{
			it->visible = visible;
			dirty = true;
		}
		if (dirty)
		{
			member.dirty = true;
			this->fresh();
		}
		return true;
	}
	return false;
}
bool BTextEdit::lineVisible(int line) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
		return it->visible;
	return false;
}

bool BTextEdit::setTextFormat(BTextFormat* textFormat)
{
	if (member.textFormat != textFormat)
	{
		BHolder<BTextFormat> holder(member.textFormat);
		member.textFormat = textFormat;
		if (BTextFormat* format = holder)
		{
			member.disconnect(format);
			format_member(format)->setWidget(0);
		}
		if (BTextFormat* format = member.textFormat)
		{
			member.connect(format, Signal_Dirty, &member_BTextEdit::slotTextFormatDirty);
			format_member(format)->setWidget(this);
		}
		for (auto it = member.blocklst.begin(); it.valid(); ++it)
		{
			if (it->format.empty())
			{
				it->dirty = true;
			}
		}
		if (textFormat)
		{
			textFormat->styled(this->realStyle());
		}
		member.freshFormat();
		member.dirty = true;
		this->fresh();
		this->emit(Signal_FormatChanged, textFormat);
		return true;
	}
	return false;
}
const BTextFormat* BTextEdit::textFormat() const
{
	return member.textFormat;
}
BTextFormat* BTextEdit::textFormat()
{
	return member.textFormat;
}

bool BTextEdit::setLineFormat(int line, BTextFormat* format)
{
	auto it = member.blocklst.seek(line);
	if (it.valid() && it->format != format)
	{
		it->format = format;
		it->dirty = true;
		this->emit(Signal_FormatChanged, format);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BTextFormat* BTextEdit::lineFormat(int line) const
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
		return it->format;
	return 0;
}
BTextFormat* BTextEdit::lineFormat(int line)
{
	auto it = member.blocklst.seek(line);
	if (it.valid())
		return it->format;
	return 0;
}

bool BTextEdit::redo()
{
	if (member.operaIndex < member.operaStack.size())
	{
		BReal now = bTime();
		BReal time = member.operaStack[member.operaIndex]->time;
		while (member.operaIndex < member.operaStack.size())
		{
			BOpera* opera = member.operaStack[member.operaIndex];
			if (opera->time != time)
				break;
			if (opera->mode == BOpera::Mode_Input)
			{
				member.inputBlock = member.blocklst.seek(opera->line);
				member.inputBlock->index = opera->index;
				member.insertText(opera->text);
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Insert)
			{
				member.inputBlock = member.blocklst.seek(opera->line);
				member.inputBlock->index = opera->index;
				member.insertText(opera->text);
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Remove)
			{
				member.selectBlocks(opera->line, opera->index, opera->text.count());
				member.removeSelection();
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Delete)
			{
				member.selectBlocks(opera->line, opera->index, opera->text.count());
				member.removeSelection();
				member.emitEdited(now);
			}
			else
			{
				opera->redo();
			}
			member.operaIndex++;
			this->emit(Signal_Opera, opera);
		}
		return true;
	}
	return false;
}
bool BTextEdit::undo()
{
	if (member.operaIndex > 0 && member.operaStack.capacity())
	{
		BReal now = bTime();
		BReal time = member.operaStack[member.operaIndex - 1]->time;
		while (member.operaIndex > 0)
		{
			BOpera* opera = member.operaStack[member.operaIndex - 1];
			if (opera->time != time)
				break;
			if (opera->mode == BOpera::Mode_Input)
			{
				member.selectBlocks(opera->line, opera->index, opera->text.count());
				member.removeSelection();
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Insert)
			{
				member.selectBlocks(opera->line, opera->index, opera->text.count());
				member.removeSelection();
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Remove)
			{
				member.inputBlock = member.blocklst.seek(opera->line);
				member.inputBlock->index = opera->index;
				member.insertText(opera->text);
				member.emitEdited(now);
			}
			else if (opera->mode == BOpera::Mode_Delete)
			{
				member.inputBlock = member.blocklst.seek(opera->line);
				member.inputBlock->index = opera->index;
				member.insertText(opera->text);
				member.inputBlock->index = opera->index;
				member.emitEdited(now);
			}
			else
			{
				opera->redo();
			}
			member.operaIndex--;
			this->emit(Signal_Opera, opera);
		}
		return true;
	}
	return false;
}

bool BTextEdit::pushOpera(BOpera* opera)
{
	if (member.stackSize == 0)
		return false;
	if (member.operaStack.contain(opera))
		return false;
	if (member.operaIndex < member.operaStack.size())
	{
		member.operaStack.remove(member.operaIndex, member.operaStack.size());
	}
	if (member.stackSize > 0 && member.operaStack.size() == member.stackSize)
	{
		member.operaStack.remove(0, 1);
		member.operaStack.push(opera);
	}
	else
	{
		member.operaStack.push(opera);
		member.operaIndex++;
	}
	this->emit(Signal_Opera, opera);
	return true;
}
bool BTextEdit::removeOpera(BOpera* opera)
{
	if (member.stackSize == 0)
		return false;
	int index = member.operaStack.index(opera);
	if (index < member.operaStack.size())
	{
		member.operaStack.remove(index, 1);
		member.operaIndex--;
		return true;
	}
	return false;
}

int BTextEdit::operaIndex() const
{
	return member.operaIndex;
}
int BTextEdit::operaCount() const
{
	return member.operaStack.size();
}
const BOpera* BTextEdit::opera(int index) const
{
	return member.operaStack(index);
}
bool BTextEdit::clearOperas()
{
	if (member.operaStack.size())
	{
		member.operaIndex = 0;
		member.operaStack.clear();
		return true;
	}
	return false;
}

bool BTextEdit::scrollToLine(int line, Align align)
{
	if (member.blocklst.check(line))
	{
		ILineBlock& block = member.blocklst[line];
		return this->scrollToRect(block.rect, align);
	}
	if (member.blocklst.size())
	{
		ILineBlock& block = member.blocklst.last();
		return this->scrollToRect(block.rect, align);
	}
	return false;
}
bool BTextEdit::scrollToCode(int line, int index, Align align)
{
	if (member.blocklst.check(line))
	{
		ILineBlock& block = member.blocklst[line];
		BPoint pos = block.rect.pos();
		BRect rect = block.cells(index).rect + pos;
		return this->scrollToRect(rect, align);
	}
	return false;
}
bool BTextEdit::scrollToRange(const BRange& range, Align align)
{
	ILineBlockIterator firstBlock;
	int firstIndex = -1;
	for (auto it = member.blocklst.begin(); it.valid(); it++)
	{
		if (it->range.contain(range.min()))
		{
			firstBlock = it;
			firstIndex = range.min() - it->range.min();
			break;
		}
	}
	ILineBlockIterator lastBlock;
	int lastIndex = -1;
	for (auto it = firstBlock; it.valid(); it++)
	{
		if (it->range.contain(range.max()))
		{
			lastBlock = it;
			lastIndex = range.max() - it->range.min();
			break;
		}
	}
	if (firstBlock.empty() || lastBlock.empty())
		return false;
	if (firstBlock == lastBlock)
	{
		BRect rect = firstBlock->cells[firstIndex].rect + firstBlock->rect.pos();
		for (int i = firstIndex + 1; i < lastIndex; i++)
		{
			BRect cellRect = firstBlock->cells[i].rect + firstBlock->rect.pos();
			rect.expand(cellRect);
		}
		return this->scrollToRect(rect, align);
	}
	else
	{
		BRect rect = firstBlock->cells[firstIndex].rect + firstBlock->rect.pos();
		for (int i = firstIndex + 1; i < firstBlock->cells.size(); i++)
		{
			BRect cellRect = firstBlock->cells[i].rect + firstBlock->rect.pos();
			rect.expand(cellRect);
		}
		for (auto it = firstBlock + 1; it < lastBlock; it++)
		{
			rect.expand(it->rect);
		}
		for (int i = 0; i < lastBlock->cells.size(); i++)
		{
			BRect cellRect = lastBlock->cells[i].rect + lastBlock->rect.pos();
			rect.expand(cellRect);
		}
		return this->scrollToRect(rect, align);
	}
	return true;
}

bool BTextEdit::input(const BString& text, const BValue& value)
{
	if (text == "input-line")
	{
		int line = value;
		if (line >= 0 && line < member.blocklst.size())
		{
			member.inputBlock = member.blocklst.seek(line);
			member.inputBlock->index = 0;
			member.inputBlock->count = member.inputBlock->cells.size();
			this->fresh();
			return true;
		}
		return false;
	}
	if (text == "input-index")
	{
		int index = value;
		return setInputPos(index);
	}
	if (text == "input-point")
	{
		const BPoint& point = value;
		int index = indexAt(point);
		return setInputPos(index);
	}
	if (text == "select-range")
	{
		const BRange& range = value;
		if (member.selectBlocks(range.min(), range.max()))
		{
			this->emit(Signal_SelectionChanged, range);
			this->fresh();
			return true;
		}
		return false;
	}
	return BScrollArea::input(text, value);
}
bool BTextEdit::query(const BString& text, BValue& value) const
{
	if (text == "changed")
	{
		value = member.changed;
		return true;
	}
	if (text == "input-line")
	{
		value = member.inputBlock.index();
		return true;
	}
	if (text == "input-index")
	{
		if (member.inputBlock.valid())
			value = member.inputBlock->index;
		else
			value = 0;
		return true;
	}
	if (text == "input-count")
	{
		if (member.firstBlock.valid())
		{
			int count = member.firstBlock->count;
			if (member.firstBlock < member.lastBlock)
			{
				for (auto it = member.firstBlock.next(); it <= member.lastBlock; ++it)
				{
					count += it->count;
				}
			}
			value = count;
		}
		else if (member.inputBlock.valid())
		{
			value = member.inputBlock->count;
		}
		return true;
	}
	if (text == "input-rect")
	{
		int index = member.inputBlock->index;
		value = member.inputBlock->cells[index].rect;
		return true;
	}
	if (text == "selected-range")
	{
		BRange range;
		if (member.firstBlock.valid())
		{
			int min = member.firstBlock->range.min() + member.firstBlock->index;
			int max = member.lastBlock->range.min() + member.lastBlock->index + member.lastBlock->count;
			range.set(min, max);
		}
		else if (member.inputBlock.valid())
		{
			int min = member.inputBlock->range.min() + member.inputBlock->index;
			int max = min + member.inputBlock->count;
			range.set(min, max);
		}
		value = range;
		return true;
	}
	if (text == "selected-text")
	{
		BString& text = value.fill<BString>();
		text.reset();
		text << selectedText();
		return true;
	}
	if (text == "selected-code-rects")
	{
		BRectArray& rects = value.fill<BRectArray>();
		rects.reset();
		for (auto it = member.blocklst.begin(); it.valid(); ++it)
		{
			BPoint pos = it->rect.pos();
			for (int i = 0; i < it->cells.size(); i++)
			{
				ICodeCell& cell = it->cells[i];
				rects.append(cell.rect + pos);
			}
		}
		return true;
	}
	if (text == "visual-range")
	{
		BRange& range = value.fill<BRange>();
		range.min() = member.showBegin.index();
		range.max() = member.showEnd.index();
		return true;
	}
	if (text == "visual-lines")
	{
		BIntArray& lines = value.fill<BIntArray>();
		lines.reset();
		for (auto it = member.showBegin; it < member.showEnd; it++)
		{
			if (it->visible)
				lines << it.index();
		}
		return true;
	}
	if (text == "hovered-line" || text == "line-hovered")
	{
		value = member.hoverBlock.index();
		return true;
	}
	if (text == "hovered-index" || text == "index-hovered")
	{
		value = member.hoverIndex;
		return true;
	}
	return BScrollArea::query(text, value);
}

void BTextEdit::scrolling(Orientation orientation)
{
	member.dirty = true;
	this->fresh();
	BScrollArea::scrolling(orientation);
}

void BTextEdit::updateEvent(const BEvent& event)
{
	if (this->focused() && !member.readOnly)
	{
		bool blinkShown = true;
		if (member.blinkTime > 0)
		{
			BReal now = event.time();
			BReal time = now - member.blinkTick;
			blinkShown = time <= member.blinkTime / 2;
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.blinkShown != blinkShown)
		{
			member.blinkShown = blinkShown;
			this->fresh();
		}
	}
}
void BTextEdit::resizeEvent(const BEvent& event)
{
	BScrollArea::resizeEvent(event);
	if (member.lineFeed)
	{
		for (int i = 0; i < member.blocklst.size(); i++)
		{
			member.blocklst[i].dirty = true;
		}
		member.dirty = true;
	}
}

void BTextEdit::styleEvent(const BEvent& event)
{
	BScrollArea::styleEvent(event);
	const BStyle* style = event.value();

	const BValue& textValue = style->value(Value_Text);
	if (textValue.valid())
	{
		this->setText((const BString&)textValue);
	}

	const BFont* font = style->font();
	member.lineHeight = font->size();

	style_member(style)->assign(Value_Line_Height, member.lineHeight);
	style_member(style)->assign(Value_Line_Spacing, member.lineSpacing);

	member.lineStyle = style->annex("line");
	if (const BStyle* lineStyle = member.lineStyle)
	{
		style_member(lineStyle)->assign(Value_Height, member.lineHeight);
		style_member(lineStyle)->assign(Value_Spacing, member.lineSpacing);
	}

	for (int i = 0; i < member.blocklst.size(); i++)
	{
		ILineBlock& block = member.blocklst[i];
		if (block.format)
		{
			block.format->styled(style);
		}
		block.lineHeight = member.lineHeight;
		block.lineSpacing = member.lineSpacing;
		block.font = font;
		block.dirty = true;
	}
	if (member.textFormat)
	{
		member.textFormat->styled(style);
	}

	member.dirty = true;
	this->fresh();

}
void BTextEdit::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.dirty = false;
		member.freshBlocks();
		this->emit(Signal_Flushed);
	}
	if (member.gotoInput)
	{
		BLine2 line = member.inputLine();
		BRect lineRect(line.begin());
		lineRect.expand(line.end());
		if (!this->centerRect().contain(lineRect))
		{
			BRect destRect = this->centerRect().wrap(lineRect);
			if (destRect.pos() != lineRect.pos())
			{
				BRect area = this->area();
				area.move(destRect.pos() - lineRect.pos());
				this->setArea(area);
			}
		}
		member.gotoInput = false;
	}
	BScrollArea::freshEvent(event);
}
void BTextEdit::paintEvent(const BEvent& event)
{
	BScrollArea::paintEvent(event);

	BPainter painter(this);
	State state = painter.state();

	painter.setClip(this->centerRect());

	painter.setStyle(member.lineStyle);
	for (auto it = member.showBegin; it < member.showEnd; it++)
	{
		if (it->visible)
		{
			State lineState = state;
			if (member.inputBlock == it || (it >= member.firstBlock && it <= member.lastBlock))
			{
				lineState = State_Checked;
			}
			if (member.hoverBlock == it)
			{
				if (member.pressed)
					lineState |= State_Pressed;
				else
					lineState |= State_Hovered;
			}
			if (painter.setColor(Color_Line, lineState))
			{
				painter.fillRect(it->rect);
			}
		}
	}
	painter.setStyle(0);

	for (auto it = member.showBegin; it < member.showEnd; it++)
	{
		if (it->visible)
		{
			BPoint pos = it->rect.pos();
			for (int i = 0; i < it->cells.size(); i++)
			{
				const ICodeCell& cell = it->cells[i];
				if (BTextFormat* format = it->format)
				{
					const BColor* background = format->ground(i);
					if (background && background->a())
					{
						painter.setColor(*background);
						painter.fillRect(cell.rect + pos);
					}
				}
				else if (member.textFormat)
				{
					const BColor* background = member.textFormat->ground(it->range.min() + i);
					if (background && background->a())
					{
						painter.setColor(*background);
						painter.fillRect(cell.rect + pos);
					}
				}
			}
		}
	}

	if (member.firstBlock.valid())
	{
		painter.setColor(Color_Selection, state);
		for (auto it = member.firstBlock; it <= member.lastBlock; ++it)
		{
			if (it->visible)
			{
				int index = it->index;
				BPoint pos = it->rect.pos();
				for (int i = 0; i < it->count; i++)
				{
					ICodeCell& cell = it->cells[index + i];
					painter.fillRect(cell.rect + pos);
				}
			}
		}
	}
	else
	{
		if (member.inputBlock.valid() && member.inputBlock->count)
		{
			painter.setColor(Color_Selection, state);
			int index = member.inputBlock->index;
			BPoint pos = member.inputBlock->rect.pos();
			for (int i = 0; i < member.inputBlock->count; i++)
			{
				ICodeCell& cell = member.inputBlock->cells[index + i];
				painter.fillRect(cell.rect + pos);
			}
		}
	}

	for (auto it = member.showBegin; it < member.showEnd; it++)
	{
		if (it->visible)
		{
			State textState = state;
			if (member.inputBlock == it)
				textState |= State_Selected;
			else if (member.hoverBlock == it)
			{
				if (member.pressed)
					textState |= State_Pressed;
				else
					textState |= State_Hovered;
			}
			if (painter.setColor(Color_Text, textState))
				it->paint(painter, member.textFormat);
		}
	}

	painter.setClip(this->size());

	if (!member.readOnly && member.blinkShown && member.inputBlock.valid() && member.inputBlock->index >= 0 && member.inputBlock->count == 0)
	{
		painter.setColor(Color_Indicator);
		BLine2 line = member.inputLine();
		painter.drawLine(line.begin(), line.end());
	}
	if (0)//member.hoverBlock.valid())
	{
		painter.setColor(200, 0, 0);
		if (member.hoverBlock->cells.check(member.hoverIndex))
		{
			ICodeCell& cell = member.hoverBlock->cells[member.hoverIndex];
			BRect rect = cell.rect + member.hoverBlock->rect.pos();
			painter.drawLine(rect.leftTop(), rect.leftBottom());
		}
		else if (member.hoverIndex == 0 || member.hoverBlock->cells.empty())
		{
			BRect rect = member.hoverBlock->rect;
			painter.drawLine(rect.leftTop(), rect.leftBottom());
		}
		else if (member.hoverIndex == member.hoverBlock->codeCount())
		{
			ICodeCell& cell = member.hoverBlock->cells.last();
			BRect rect = cell.rect + member.hoverBlock->rect.pos();
			painter.drawLine(rect.rightTop(), rect.rightBottom());
		}
	}
}

void BTextEdit::focusEvent(const BEvent& event)
{
	if (!this->focused())
	{
		if (member.editing)
		{
			member.editing = false;
			if (this->checks(Signal_EditFinished))
				this->emit(Signal_EditFinished, this->text());
		}
		member.blinkShown = false;
		member.dirty = true;
		this->fresh();
	}
	BScrollArea::focusEvent(event);
}
void BTextEdit::inputEvent(const BEvent& event)
{
	if (!member.readOnly && member.inputBlock.valid())
	{
		const BCode& code = event.value();
		if (member.hasSelection())
		{
			member.operaRemove(event.time());
			member.removeSelection();
		}
		member.operaInput(event.time(), code);
		member.insertCode(code);
		member.emitEdited(event.time());
		BLine2 line = member.inputLine();
		event.accept(line.end());
	}
	BScrollArea::inputEvent(event);
}

void BTextEdit::keyPressEvent(const BKeyEvent& keyEvent)
{
	bool ctrlDown = keyEvent.keyState(Key_Ctrl);
	if (ctrlDown)
	{
		if (keyEvent.key() == Key_C)
		{
			BString text = selectedText();
			BSystem::SetClippedText(text);
			keyEvent.accept(true);
		}
		if (keyEvent.key() == Key_A)
		{
			member.firstBlock = member.blocklst.begin();
			member.lastBlock = member.blocklst.rbegin();
			for (auto it = member.blocklst.begin(); it.valid(); ++it)
			{
				it->index = 0;
				it->count = it->cells.size();
			}
			member.blinkShown = false;
			this->fresh();
			keyEvent.accept(true);
		}
	}
	if (member.readOnly == false)
	{
		if (keyEvent.key() == Key_Z && ctrlDown)
		{
			bool shitfDown = keyEvent.keyState(Key_Shift);
			if (shitfDown)
			{
				this->redo();
			}
			else
			{
				this->undo();
			}
		}
		else if (keyEvent.key() == Key_V && ctrlDown)
		{
			BString text;
			BSystem::GetClippedText(text);
			if (text.size())
			{
				if (member.hasSelection())
				{
					member.operaRemove(keyEvent.time());
					member.removeSelection();
				}
				member.operaInsert(keyEvent.time(), text);
				member.insertText(text);
				member.emitEdited(keyEvent.time());
			}
			keyEvent.accept(true);
		}
		else if (keyEvent.key() == Key_X && ctrlDown)
		{
			BString text = this->selectedText();
			if (text.size())
			{
				member.operaRemove(keyEvent.time());
				BSystem::SetClippedText(text);
				if (member.removeSelection())
				{
					member.emitEdited(keyEvent.time());
				}
			}
			keyEvent.accept(true);
		}
		else if (keyEvent.key() == Key_Left)
		{
			if (member.firstBlock.valid())
			{
				member.inputBlock->count = 0;
				member.firstBlock->count = 0;
				member.lastBlock->count = 0;
				member.firstBlock.reset();
				member.lastBlock.reset();
				member.inputBlock = member.firstBlock;
			}
			else if (member.inputBlock->count > 0)
			{
				member.inputBlock->count = 0;
			}
			else
			{
				if (ctrlDown)
					member.seekPrevWord();
				else
					member.seekPrev();
			}
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			member.wrapInputLine();
		}
		else if (keyEvent.key() == Key_Right)
		{
			if (member.firstBlock.valid())
			{
				member.inputBlock->count = 0;
				member.firstBlock->count = 0;
				member.lastBlock->count = 0;
				member.firstBlock.reset();
				member.lastBlock.reset();
				member.inputBlock = member.lastBlock;
			}
			else if (member.inputBlock->count > 0)
			{
				member.inputBlock->index += member.inputBlock->count;
				member.inputBlock->count = 0;
			}
			else
			{
				if (ctrlDown)
					member.seekNextWord();
				else
					member.seekNext();
			}
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			member.wrapInputLine();
		}
		else if (keyEvent.key() == Key_Up)
		{
			if (member.seekUp())
				member.wrapInputLine();
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
		}
		else if (keyEvent.key() == Key_Down)
		{
			if (member.seekDown())
				member.wrapInputLine();
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
		}
		else if (keyEvent.key() == Key_Tab)
		{
			if (keyEvent.keyState(Key_Shift))
			{
				if (member.firstBlock.valid())
				{
					auto headBlock = member.firstBlock;
					auto tailBlock = member.lastBlock;
					for (auto it = headBlock; it <= tailBlock; ++it)
					{
						if (it->text.beginWith('\t'))
						{
							setInputPos(it.index(), 0);
							member.operaRemove(keyEvent.time());
							it->remove(0);
						}
					}
					this->selectLines(headBlock.index(), tailBlock.index());
					member.emitEdited(keyEvent.time());
				}
				else if (member.inputBlock->count == member.inputBlock->cells.size())
				{
					if (member.inputBlock->text.beginWith('\t'))
					{
						int line = member.inputBlock.index();
						setInputPos(line, 0);
						member.selectBlocks(member.inputBlock.index(), 0, 1);
						member.operaRemove(keyEvent.time());
						member.inputBlock->remove(0);
						this->selectLines(line, line);
						member.emitEdited(keyEvent.time());
					}
				}
			}
			else
			{
				if (member.firstBlock.valid())
				{
					auto headBlock = member.firstBlock;
					auto tailBlock = member.lastBlock;
					for (auto it = headBlock; it <= tailBlock; ++it)
					{
						setInputPos(it.index(), 0);
						member.operaInsert(keyEvent.time(), '\t');
						it->prepend(BCode('\t'));
					}
					this->selectLines(headBlock.index(), tailBlock.index());
					member.emitEdited(keyEvent.time());
				}
				else if (member.inputBlock->count == member.inputBlock->cells.size())
				{
					int line = member.inputBlock.index();
					setInputPos(line, 0);
					member.operaInsert(keyEvent.time(), '\t');
					member.inputBlock->prepend(BCode('\t'));
					this->selectLines(line, line);
					member.emitEdited(keyEvent.time());
				}
				else
				{
					if (member.hasSelection())
					{
						member.operaRemove(keyEvent.time());
						member.removeSelection();
					}
					member.operaInsert(keyEvent.time(), '\t');
					member.insertCode(BCode('\t'));
					member.emitEdited(keyEvent.time());
				}
			}
		}
		else if (keyEvent.key() == Key_Enter)
		{
			if (member.hasSelection())
			{
				member.operaRemove(keyEvent.time());
				member.removeSelection();
			}
			member.operaInsert(keyEvent.time(), '\n');
			member.insertCode(BCode('\n'));
			member.emitEdited(keyEvent.time());
		}
		else if (keyEvent.key() == Key_Delete)
		{
			if (member.hasSelection())
			{
				member.operaRemove(keyEvent.time());
				member.removeSelection();
				member.emitEdited(keyEvent.time());
			}
			else if (ctrlDown)
			{
				member.pressBlock = member.inputBlock;
				member.pressIndex = member.inputBlock->index;
				if (member.seekNextWord())
				{
					member.hoverBlock = member.inputBlock;
					member.hoverIndex = member.inputBlock->index;
				}
				if (member.selectBlocks())
				{
					member.operaRemove(keyEvent.time());
					member.removeSelection();
					member.emitEdited(keyEvent.time());
				}
			}
			else
			{
				member.selectBlocks(member.inputBlock.index(), member.inputBlock->index, 1);
				if (member.hasSelection())
				{
					member.operaDelete(keyEvent.time());
					member.removeSelection();
					member.emitEdited(keyEvent.time());
				}
			}
		}
		else if (keyEvent.key() == Key_Back)
		{
			if (member.hasSelection())
			{
				member.operaRemove(keyEvent.time());
				member.removeSelection();
				member.emitEdited(keyEvent.time());
			}
			else if (ctrlDown)
			{
				member.pressBlock = member.inputBlock;
				member.pressIndex = member.inputBlock->index;
				if (member.seekPrevWord())
				{
					member.hoverBlock = member.inputBlock;
					member.hoverIndex = member.inputBlock->index;
				}
				if (member.selectBlocks())
				{
					member.operaRemove(keyEvent.time());
					member.removeSelection();
					member.emitEdited(keyEvent.time());
				}
			}
			else if (member.seekPrev())
			{
				member.inputBlock->count = 1;
				member.operaRemove(keyEvent.time());
				member.removeSelection();
				member.emitEdited(keyEvent.time());
			}
		}
		else if (keyEvent.key() == Key_Home)
		{
			member.seekHome();
			member.wrapInputLine();
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
		}
		else if (keyEvent.key() == Key_End)
		{
			member.seekEnd();
			member.wrapInputLine();
			member.blinkShown = true;
			member.blinkTick = keyEvent.time() + (BReal)0.5;
		}
	}
	BScrollArea::keyPressEvent(keyEvent);
}
void BTextEdit::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	BScrollArea::keyReleaseEvent(keyEvent);
}

void BTextEdit::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverBlock.valid())
	{
		member.hoverBlock.reset();
		this->fresh();
	}
	BScrollArea::mouseEnterEvent(mouseEvent);
}
void BTextEdit::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverBlock.valid())
	{
		member.hoverBlock.reset();
		this->fresh();
	}
	BScrollArea::mouseLeaveEvent(mouseEvent);
}

void BTextEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left)
	{
		if (mouseEvent.keyState(Key_Ctrl))
		{
			member.inputBlock = member.hoverBlock;
			member.inputBlock->index = member.hoverIndex;
			if (member.inputBlock.valid())
			{
				member.inputBlock->selectWord();
			}
		}
		else if (mouseEvent.keyState(Key_Shift))
		{
			if (this->rect(Part_Left).contain(pos))
			{
				member.selectLines();
			}
			else
			{
				member.pressBlock = member.inputBlock;
				member.selectBlocks();
			}
		}
		else
		{
			member.unselect();
			if (this->rect(Part_Left).contain(pos))
			{
				member.lineSelecting = true;
				member.inputBlock = member.hoverBlock;
				if (member.inputBlock.valid())
				{
					member.inputBlock->index = 0;
					member.inputBlock->count = member.inputBlock->cells.size();
				}
			}
			else
			{
				member.inputBlock = member.hoverBlock;
				if (member.inputBlock.valid())
				{
					member.inputBlock->index = member.hoverIndex;
					member.inputBlock->count = 0;
				}
				member.blinkShown = true;
				member.blinkTick = mouseEvent.time() + (BReal)0.5;
			}
			member.pressed = true;
			member.pressBlock = member.hoverBlock;
			member.pressIndex = member.hoverIndex;
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
		}
		this->fresh();
	}
	BScrollArea::mousePressEvent(mouseEvent);
}
void BTextEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.pressed)
		{
			member.pressed = false;
			member.lineSelecting = false;
		}
	}
	BScrollArea::mouseReleaseEvent(mouseEvent);
}
void BTextEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (member.pressed && member.lineSelecting)
	{
		ILineBlockIterator hoverBlock;
		if (member.blocklst.size())
		{
			hoverBlock = member.blocklst.rbegin();
			for (auto it = member.showBegin; it < member.showEnd; it++)
			{
				const BRect& rect = it->rect;
				if (rect.top() <= pos.y() && rect.bottom() >= pos.y())
				{
					hoverBlock = it;
					break;
				}
				if (rect.top() > pos.y())
				{
					hoverBlock = it;
					break;
				}
			}
		}
		if (member.hoverBlock != hoverBlock)
		{
			member.hoverBlock = hoverBlock;
			if (member.pressed)
			{
				member.unselect();
				member.selectLines();
				if (this->checks(Signal_SelectionChanged))
				{
					BRange range;
					member.selectedRange(range);
					this->emit(Signal_SelectionChanged, range);
				}
			}
			this->fresh();
		}
	}
	else
	{
		int	hoverIndex = -1;
		ILineBlockIterator hoverBlock;
		if (member.blocklst.size())
		{
			hoverBlock = member.blocklst.rbegin();
			hoverIndex = hoverBlock->cells.size();
			for (auto it = member.showBegin; it < member.showEnd; it++)
			{
				if (!it->visible)
					continue;
				const BRect& rect = it->rect;
				if (rect.top() <= pos.y() && rect.bottom() >= pos.y())
				{
					hoverBlock = it;
					hoverIndex = hoverBlock->indexAt(pos);
					break;
				}
				if (rect.top() > pos.y())
				{
					hoverBlock = it;
					hoverIndex = 0;
					break;
				}
			}
			if (hoverBlock->cells(hoverIndex - 1) == '\n')
				hoverIndex--;
			if (hoverBlock->cells(hoverIndex - 1) == '\r')
				hoverIndex--;
		}
		bool diffBlock = member.hoverBlock != hoverBlock;
		if (diffBlock || member.hoverIndex != hoverIndex)
		{
			member.hoverBlock = hoverBlock;
			member.hoverIndex = hoverIndex;
			if (member.pressed)
			{
				member.unselect();
				member.selectBlocks();
				member.inputBlock = member.hoverBlock;
				if (this->checks(Signal_SelectionChanged))
				{
					BRange range;
					member.selectedRange(range);
					this->emit(Signal_SelectionChanged, range);
				}
			}
			this->fresh();
		}
		if (diffBlock)
		{
			this->emit(Signal_LineHovered, hoverBlock.index());
		}
	}
	if (mouseEvent.button() == Button_Left)
	{
		BRect upRect = this->centerRect();
		upRect.bottom() = upRect.top() + 20;
		if (upRect.contain(pos))
		{
			this->stepScrollTo(Part_Top);
		}

		BRect downRect = this->centerRect();
		downRect.top() = downRect.bottom() - 20;
		if (downRect.contain(pos))
		{
			this->stepScrollTo(Part_Bottom);
		}
	}
	if (mouseEvent.button() == Button_None && this->focusable())
	{
		const BValue& value = mouseEvent.result();
		if (value.valid())
		{
			if (value.is<CursorType>())
				this->setCursor((CursorType)value);
			else if (BCursor* cursor = value)
				this->setCursor(cursor);
		}
		else
		{
			const BRect& crect = this->centerRect();
			if (crect.contain(pos))
				this->setCursor(Cursor_Ibeam);
			else
				this->setCursor(Cursor_None);
		}
	}
	BScrollArea::mouseMoveEvent(mouseEvent);
}
void BTextEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.inputBlock.valid())
		{
			member.inputBlock->selectWord();
			member.pressBlock = member.inputBlock;
			if (member.pressIndex != member.inputBlock->index)
			{
				member.pressIndex = member.inputBlock->index;
			}
			if (this->checks(Signal_SelectionChanged))
			{
				BRange range;
				member.selectedRange(range);
				this->emit(Signal_SelectionChanged, range);
			}
			this->fresh();
		}
	}
	BScrollArea::mouseDoubleClickEvent(mouseEvent);
}
